//     __ _____ _____ _____
//  __|  |   __|     |   | |  JSON for Modern C++
// |  |  |__   |  |  | | | |  version 3.11.3
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT

#pragma once

#include <cstddef>   // ptrdiff_t
#include <iterator>  // reverse_iterator
#include <nlohmann/detail/abi_macros.hpp>
#include <utility>  // declval

NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail {

//////////////////////
// reverse_iterator //
//////////////////////

/*!
@brief a template for a reverse iterator class

@tparam Base the base iterator type to reverse. Valid types are @ref
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
create @ref const_reverse_iterator).

@requirement The class satisfies the following concept requirements:
-
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
  The iterator that can be moved can be moved in both directions (i.e.
  incremented and decremented).
- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
  It is possible to write to the pointed-to element (only if @a Base is
  @ref iterator).

@since version 1.0.0
*/
template <typename Base>
class json_reverse_iterator : public std::reverse_iterator<Base> {
 public:
  using difference_type = std::ptrdiff_t;
  /// shortcut to the reverse iterator adapter
  using base_iterator = std::reverse_iterator<Base>;
  /// the reference type for the pointed-to element
  using reference = typename Base::reference;

  /// create reverse iterator from iterator
  explicit json_reverse_iterator(
      const typename base_iterator::iterator_type &it) noexcept
      : base_iterator(it) {}

  /// create reverse iterator from base class
  explicit json_reverse_iterator(const base_iterator &it) noexcept
      : base_iterator(it) {}

  /// post-increment (it++)
  json_reverse_iterator operator++(int) &  // NOLINT(cert-dcl21-cpp)
  {
    return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
  }

  /// pre-increment (++it)
  json_reverse_iterator &operator++() {
    return static_cast<json_reverse_iterator &>(base_iterator::operator++());
  }

  /// post-decrement (it--)
  json_reverse_iterator operator--(int) &  // NOLINT(cert-dcl21-cpp)
  {
    return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
  }

  /// pre-decrement (--it)
  json_reverse_iterator &operator--() {
    return static_cast<json_reverse_iterator &>(base_iterator::operator--());
  }

  /// add to iterator
  json_reverse_iterator &operator+=(difference_type i) {
    return static_cast<json_reverse_iterator &>(base_iterator::operator+=(i));
  }

  /// add to iterator
  json_reverse_iterator operator+(difference_type i) const {
    return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
  }

  /// subtract from iterator
  json_reverse_iterator operator-(difference_type i) const {
    return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
  }

  /// return difference
  difference_type operator-(const json_reverse_iterator &other) const {
    return base_iterator(*this) - base_iterator(other);
  }

  /// access to successor
  reference operator[](difference_type n) const {
    return *(this->operator+(n));
  }

  /// return the key of an object iterator
  auto key() const -> decltype(std::declval<Base>().key()) {
    auto it = --this->base();
    return it.key();
  }

  /// return the value of an iterator
  reference value() const {
    auto it = --this->base();
    return it.operator*();
  }
};

}  // namespace detail
NLOHMANN_JSON_NAMESPACE_END
